﻿// Spun AJAX Framework
// Copyright (C) 2008 Blake Hegerle
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Formality.Parsing.ContextFree
{
    public interface INonterminal : ISymbol
    {
        IEnumerable<Production> Productions { get; }
    }

    public class Nonterminal<S> : Symbol<S>, INonterminal
    {
        public Nonterminal(string Name)
            : base(Name)
        {
            this.productions = new List<Production>();
        }

        public Production add(Func<S> SemanticFunc)
        { return add(new Production { Head = this, SemanticFunc = SemanticFunc, Symbols = new Symbol[0] }); }

        public Production add<R1>(Symbol<R1> r1, Func<R1, S> SemanticFunc)
        { return add(new Production { Head = this, SemanticFunc = SemanticFunc, Symbols = new Symbol[] { r1 } }); }
        public Production add<R1, R2>(Symbol<R1> r1, Symbol<R2> r2, Func<R1, R2, S> SemanticFunc)
        { return add(new Production { Head = this, SemanticFunc = SemanticFunc, Symbols = new Symbol[] { r1, r2 } }); }
        public Production add<R1, R2, R3>(Symbol<R1> r1, Symbol<R2> r2, Symbol<R3> r3, Func<R1, R2, R3, S> SemanticFunc)
        { return add(new Production { Head = this, SemanticFunc = SemanticFunc, Symbols = new Symbol[] { r1, r2, r3 } }); }
        public Production add<R1, R2, R3, R4>(Symbol<R1> r1, Symbol<R2> r2, Symbol<R3> r3, Symbol<R4> r4, Func<R1, R2, R3, R4, S> SemanticFunc)
        { return add(new Production { Head = this, SemanticFunc = SemanticFunc, Symbols = new Symbol[] { r1, r2, r3, r4 } }); }
        public Production add<R1, R2, R3, R4, R5>(Symbol<R1> r1, Symbol<R2> r2, Symbol<R3> r3, Symbol<R4> r4, Symbol<R5> r5, Func<R1, R2, R3, R4, R5, S> SemanticFunc)
        { return add(new Production { Head = this, SemanticFunc = SemanticFunc, Symbols = new Symbol[] { r1, r2, r3, r4, r5 } }); }
        public Production add<R1, R2, R3, R4, R5, R6>(Symbol<R1> r1, Symbol<R2> r2, Symbol<R3> r3, Symbol<R4> r4, Symbol<R5> r5, Symbol<R6> r6, Func<R1, R2, R3, R4, R5, R6, S> SemanticFunc)
        { return add(new Production { Head = this, SemanticFunc = SemanticFunc, Symbols = new Symbol[] { r1, r2, r3, r4, r5, r6 } }); }
        public Production add<R1, R2, R3, R4, R5, R6, R7>(Symbol<R1> r1, Symbol<R2> r2, Symbol<R3> r3, Symbol<R4> r4, Symbol<R5> r5, Symbol<R6> r6, Symbol<R7> r7, Func<R1, R2, R3, R4, R5, R6, R7, S> SemanticFunc)
        { return add(new Production { Head = this, SemanticFunc = SemanticFunc, Symbols = new Symbol[] { r1, r2, r3, r4, r5, r6, r7 } }); }
        public Production add<R1, R2, R3, R4, R5, R6, R7, R8>(Symbol<R1> r1, Symbol<R2> r2, Symbol<R3> r3, Symbol<R4> r4, Symbol<R5> r5, Symbol<R6> r6, Symbol<R7> r7, Symbol<R8> r8, Func<R1, R2, R3, R4, R5, R6, R7, R8, S> SemanticFunc)
        { return add(new Production { Head = this, SemanticFunc = SemanticFunc, Symbols = new Symbol[] { r1, r2, r3, r4, r5, r6, r7, r8 } }); }
        public Production add<R1, R2, R3, R4, R5, R6, R7, R8, R9>(Symbol<R1> r1, Symbol<R2> r2, Symbol<R3> r3, Symbol<R4> r4, Symbol<R5> r5, Symbol<R6> r6, Symbol<R7> r7, Symbol<R8> r8, Symbol<R9> r9, Func<R1, R2, R3, R4, R5, R6, R7, R8, R9, S> SemanticFunc)
        { return add(new Production { Head = this, SemanticFunc = SemanticFunc, Symbols = new Symbol[] { r1, r2, r3, r4, r5, r6, r7, r8, r9 } }); }
        public Production add<R1, R2, R3, R4, R5, R6, R7, R8, R9, R10>(Symbol<R1> r1, Symbol<R2> r2, Symbol<R3> r3, Symbol<R4> r4, Symbol<R5> r5, Symbol<R6> r6, Symbol<R7> r7, Symbol<R8> r8, Symbol<R9> r9, Symbol<R10> r10, Func<R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, S> SemanticFunc)
        { return add(new Production { Head = this, SemanticFunc = SemanticFunc, Symbols = new Symbol[] { r1, r2, r3, r4, r5, r6, r7, r8, r9, r10 } }); }
        public Production add<R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11>(Symbol<R1> r1, Symbol<R2> r2, Symbol<R3> r3, Symbol<R4> r4, Symbol<R5> r5, Symbol<R6> r6, Symbol<R7> r7, Symbol<R8> r8, Symbol<R9> r9, Symbol<R10> r10, Symbol<R11> r11, Func<R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, S> SemanticFunc)
        { return add(new Production { Head = this, SemanticFunc = SemanticFunc, Symbols = new Symbol[] { r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11 } }); }
        public Production add<R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12>(Symbol<R1> r1, Symbol<R2> r2, Symbol<R3> r3, Symbol<R4> r4, Symbol<R5> r5, Symbol<R6> r6, Symbol<R7> r7, Symbol<R8> r8, Symbol<R9> r9, Symbol<R10> r10, Symbol<R11> r11, Symbol<R12> r12, Func<R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, S> SemanticFunc)
        { return add(new Production { Head = this, SemanticFunc = SemanticFunc, Symbols = new Symbol[] { r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12 } }); }
        public Production add<R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13>(Symbol<R1> r1, Symbol<R2> r2, Symbol<R3> r3, Symbol<R4> r4, Symbol<R5> r5, Symbol<R6> r6, Symbol<R7> r7, Symbol<R8> r8, Symbol<R9> r9, Symbol<R10> r10, Symbol<R11> r11, Symbol<R12> r12, Symbol<R13> r13, Func<R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, S> SemanticFunc)
        { return add(new Production { Head = this, SemanticFunc = SemanticFunc, Symbols = new Symbol[] { r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13 } }); }
        public Production add<R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14>(Symbol<R1> r1, Symbol<R2> r2, Symbol<R3> r3, Symbol<R4> r4, Symbol<R5> r5, Symbol<R6> r6, Symbol<R7> r7, Symbol<R8> r8, Symbol<R9> r9, Symbol<R10> r10, Symbol<R11> r11, Symbol<R12> r12, Symbol<R13> r13, Symbol<R14> r14, Func<R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, S> SemanticFunc)
        { return add(new Production { Head = this, SemanticFunc = SemanticFunc, Symbols = new Symbol[] { r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14 } }); }
        public Production add<R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R15>(Symbol<R1> r1, Symbol<R2> r2, Symbol<R3> r3, Symbol<R4> r4, Symbol<R5> r5, Symbol<R6> r6, Symbol<R7> r7, Symbol<R8> r8, Symbol<R9> r9, Symbol<R10> r10, Symbol<R11> r11, Symbol<R12> r12, Symbol<R13> r13, Symbol<R14> r14, Symbol<R15> r15, Func<R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R15, S> SemanticFunc)
        { return add(new Production { Head = this, SemanticFunc = SemanticFunc, Symbols = new Symbol[] { r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15 } }); }
        public Production add<R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R15, R16>(Symbol<R1> r1, Symbol<R2> r2, Symbol<R3> r3, Symbol<R4> r4, Symbol<R5> r5, Symbol<R6> r6, Symbol<R7> r7, Symbol<R8> r8, Symbol<R9> r9, Symbol<R10> r10, Symbol<R11> r11, Symbol<R12> r12, Symbol<R13> r13, Symbol<R14> r14, Symbol<R15> r15, Symbol<R16> r16, Func<R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R15, R16, S> SemanticFunc)
        { return add(new Production { Head = this, SemanticFunc = SemanticFunc, Symbols = new Symbol[] { r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16 } }); }
        public Production add<R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R15, R16, R17>(Symbol<R1> r1, Symbol<R2> r2, Symbol<R3> r3, Symbol<R4> r4, Symbol<R5> r5, Symbol<R6> r6, Symbol<R7> r7, Symbol<R8> r8, Symbol<R9> r9, Symbol<R10> r10, Symbol<R11> r11, Symbol<R12> r12, Symbol<R13> r13, Symbol<R14> r14, Symbol<R15> r15, Symbol<R16> r16, Symbol<R17> r17, Func<R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R15, R16, R17, S> SemanticFunc)
        { return add(new Production { Head = this, SemanticFunc = SemanticFunc, Symbols = new Symbol[] { r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16, r17 } }); }
        public Production add<R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R15, R16, R17, R18>(Symbol<R1> r1, Symbol<R2> r2, Symbol<R3> r3, Symbol<R4> r4, Symbol<R5> r5, Symbol<R6> r6, Symbol<R7> r7, Symbol<R8> r8, Symbol<R9> r9, Symbol<R10> r10, Symbol<R11> r11, Symbol<R12> r12, Symbol<R13> r13, Symbol<R14> r14, Symbol<R15> r15, Symbol<R16> r16, Symbol<R17> r17, Symbol<R18> r18, Func<R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R15, R16, R17, R18, S> SemanticFunc)
        { return add(new Production { Head = this, SemanticFunc = SemanticFunc, Symbols = new Symbol[] { r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16, r17, r18 } }); }
        public Production add<R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R15, R16, R17, R18, R19>(Symbol<R1> r1, Symbol<R2> r2, Symbol<R3> r3, Symbol<R4> r4, Symbol<R5> r5, Symbol<R6> r6, Symbol<R7> r7, Symbol<R8> r8, Symbol<R9> r9, Symbol<R10> r10, Symbol<R11> r11, Symbol<R12> r12, Symbol<R13> r13, Symbol<R14> r14, Symbol<R15> r15, Symbol<R16> r16, Symbol<R17> r17, Symbol<R18> r18, Symbol<R19> r19, Func<R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R15, R16, R17, R18, R19, S> SemanticFunc)
        { return add(new Production { Head = this, SemanticFunc = SemanticFunc, Symbols = new Symbol[] { r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16, r17, r18, r19 } }); }
        
        public Production add(Delegate Delegate, params Symbol[] Symbols)
        { return add(new Production { Head = this, SemanticFunc = Delegate, Symbols = Symbols }); }

        public IEnumerable<Production> Productions { get { return productions; } }

        private Production add(Production Production)
        {
            productions.Add(Production);
            return Production;
        }

        private static string Stringify(params object[] objs)
        {
            var sb = new StringBuilder();
            foreach (var o in objs)
                sb.Append(o);
            return sb.ToString();
        }

        private List<Production> productions;

        private delegate string StringDelegate(params object[] objs);
    }
}
